home *** CD-ROM | disk | FTP | other *** search
/ Night Owl 6 / Night Owl's Shareware - PDSI-006 - Night Owl Corp (1990).iso / 010a / chkfrg14.zip / CHKFRAG.C < prev    next >
Text File  |  1991-12-16  |  31KB  |  988 lines

  1. /***************************************************
  2.  *                           *
  3.  *    chkfrag - check disk for fragmentation       *
  4.  *                           *
  5.  ***************************************************/
  6.  
  7. /* ******************************************************************** *
  8.  
  9.     Maintenance log
  10.  
  11.     Version   Date   Description                Who
  12.     ------- -------- -------------------------------------- ----------
  13.     1.0 to 1.3         Various bug fixes                Flanders/Holmes
  14.     1.3
  15.  
  16.     1.4     16Dec91  DOS 5 compatibility            Flanders/Holmes
  17.               .. Read FAT greater than 64K long
  18.               .. Minor compiler error on readlabel()
  19.               .. Compiles under MSC 6.0
  20.               .. Added pack pragma to remove need for
  21.              /Zp operand on compile
  22.               (Thanks to J. Stephen Myers for his fixes
  23.                to v1.2)
  24.  
  25.  * ******************************************************************** */
  26.  
  27.  
  28. #pragma pack(1)                 /* Pack to byte alignment    */
  29.  
  30. #include <stdio.h>                /* standard library     */
  31. #include <dos.h>                /* dos access and registers */
  32. #include <malloc.h>                /* memory allocation    */
  33. #include <stdlib.h>                /* common lib modules    */
  34.  
  35. #define UINT unsigned int            /* unsigned integer type    */
  36. #define ULONG unsigned long            /* unsigned long type    */
  37. #define NOT !                    /* logical not        */
  38. #define BOOT b_rec                /* boot record shorthand    */
  39. #define FILES (_A_SYSTEM | _A_HIDDEN | _A_SUBDIR)   /* files type    */
  40. #define LABEL (_A_VOLID)                /* label type    */
  41. #define CLEAR(s,c) strclr(s,c,sizeof(s))    /* string clear        */
  42.  
  43. /*
  44.  *  Globals
  45.  */
  46.  
  47. char    huge *fat,            /* address of FAT        */
  48.     cdrive[66],            /* startup drive and path    */
  49.     fat_16;             /* true if 16 bit FAT entries    */
  50.  
  51. int    sections = 0,            /* file sections        */
  52.     secsize = 0 ,            /* sector size of drive     */
  53.     frag = 0,            /* fragmented files        */
  54.     unfrag = 0,            /* unfragmented files        */
  55.     freespaces = 0,         /* freespaces            */
  56.     files = 0,            /* processed files        */
  57.     dirs = 0,            /* processed directories    */
  58.     dos4 = 0,            /* use dos version 4 int24    */
  59.     list = 0;            /* list frag'd files switch     */
  60.  
  61. UINT    nclusters,            /* number of clusters        */
  62.     sdrive;             /* startup drive        */
  63.  
  64. long    nsectors;            /* number of sectors on drive    */
  65.  
  66. /*
  67.  *  formal declarations
  68.  */
  69.  
  70. void    get_fat(int),            /* read FAT into memory     */
  71.     cfexit(int),            /* exit routine         */
  72.     check_frag(char *, UINT, int),    /* check if file/dir is frag'd  */
  73.     dir_search(char *),        /* search directory        */
  74.     strclr(char *, int, int),    /* clear a string        */
  75.     check_unlinked();        /* check for unlinked clusters    */
  76. int    chkdrv(char);            /* check local, SUBST/ASSIGN    */
  77. char    *fname(char *, char*),        /* fcb filename to normal fname */
  78.     *readlabel(int),        /* read the drive label     */
  79.     *translate_name(char far *);    /* translate name        */
  80. long    next_cluster(UINT, int, int *); /* find the next cluster in FAT */
  81.  
  82. struct    fcb
  83.     {
  84.     char    hexff;            /* extended fcb first byte    */
  85.     char    extra[5];            /* extended fcb work area    */
  86.     char    attrib;            /* extended fcb attribute    */
  87.     char    drive;            /* fcb - drive            */
  88.     char    filename[8];        /* fcb - filename        */
  89.     char    ext[3];            /* fcb - extension        */
  90.     unsigned
  91.     int     block;            /* fcb - block number        */
  92.     unsigned
  93.     long    filesize;            /* fcb - file size        */
  94.     int     date;            /* fcb - file date        */
  95.     char    system[10];         /* fcb - reserved area        */
  96.     char    record;            /* fcb - current record     */
  97.     unsigned
  98.     long    rnd_recno;            /* fcb - random record number    */
  99.     } ;
  100.  
  101. /************************************************************************
  102.  *                                    *
  103.  *    mainline                            *
  104.  *                                    *
  105.  ************************************************************************/
  106.  
  107. main(argc, argv)
  108. int    argc;                /* count of arguments        */
  109. char    *argv[];            /* argument strings        */
  110. {
  111. long    pf = 0;             /* percent fragmented        */
  112. UINT    rc;                /* return code            */
  113. int    ctc = 0,            /* return code chosen        */
  114.     dc = 0,             /* drive chosen         */
  115.     pe = 0,             /* parm in error        */
  116.     i, j,                /* loop counter, work        */
  117.     option = 0;            /* program option        */
  118. char    *p;                /* work pointer         */
  119. static
  120. char    drive[] = " :\\",               /* drive and path to check      */
  121.     *rc_type[] =
  122.         { "Percentage",
  123.           "Number of Files",
  124.           "Number of Extra Segments",
  125.           "Number of Free Areas" },
  126.  
  127.     *suggestion[] =
  128.         { "No fragmentation found -- Defrag unnecessary",
  129.           "Little fragmentation -- Defrag optional",
  130.           "Moderate fragmentation -- Defrag should be performed soon",
  131.           "Fragmentation critical -- Defrag or Backup/Format/Restore" },
  132.  
  133.     *errors[] =
  134.         { "Invalid drive specified",
  135.           "Cannot CHKFRAG a network drive",
  136.           "Cannot CHKFRAG a SUBST'd or ASSIGN'd drive",
  137.           "Must run with DOS 2.0 or greater" },
  138.  
  139.     *options[] =
  140.          { "/%", "/N", "/E", "/F", "/L", "/4"} ;
  141.  
  142. printf("CHKFRAG 1.4 -- Copyright (c) 1991 Ziff Communications Co.\n%s%c%s\n",
  143.        "PC Magazine ", 254, " Bob Flanders & Michael Holmes\n");
  144.  
  145.  
  146. _dos_getdrive(&sdrive);             /* get the default drive    */
  147. *drive = sdrive + 'A' - 1;                  /* ..setup default drive    */
  148.  
  149.  
  150. for (i = 1; i < argc; i++)            /* check each argument    */
  151.     {
  152.     strupr(p = argv[i]);            /* uppercase argument    */
  153.  
  154.     if (strlen(p) == 2 && p[1] == ':')      /* q. drive parm specified? */
  155.     {
  156.     *drive = *p;                /* a. yes .. setup drive    */
  157.     dc++;                    /* .. show drive selected    */
  158.     }
  159.  
  160.      else
  161.     {
  162.     for (j = 0; strcmp(p, options[j])   /* search arguments     */
  163.             && (j < 6); j++);
  164.  
  165.     switch(j)                /* based on argument    */
  166.         {
  167.         case 0:                /* /% option        */
  168.         case 1:                /* /N option        */
  169.         case 2:                /* /E option        */
  170.         case 3:                /* /F option        */
  171.  
  172.         option = j;            /* set up the option value    */
  173.  
  174.         ctc++;                /* increment code type count*/
  175.         break;                /* exit switch        */
  176.  
  177.         case 4:                /* /L switch        */
  178.         list++;             /* .. show listing wanted    */
  179.         break;
  180.  
  181.         case 5:                /* /4 switch        */
  182.         dos4++;             /* .. use version 4 int25    */
  183.         break;
  184.  
  185.         case 6:                /* error            */
  186.         pe = j;             /* argument in error    */
  187.         break;                /* .. error         */
  188.         }
  189.     }
  190.     }
  191.  
  192. if (pe || (ctc > 1) || (list>1) || (dc > 1))/* q. any error?        */
  193.     {                        /* a. yes .. handle error    */
  194.     printf("\n\tformat\tCHKFRAG  [d:] [/%% | /N | /E] [/L]\n\n");
  195.     printf("\twhere\td: is the drive to check for fragmentation\n");
  196.     printf("\t\t/%% sets errorlevel as a percentage\n");
  197.     printf("\t\t/N sets errorlevel to number of fragmented files (max 254)\n");
  198.     printf("\t\t/E sets errorlevel number of extra sections (max 254)\n");
  199.     printf("\t\t/F sets errorlevel number of free space areas (max 254)\n");
  200.     printf("\t\t/L causes fragmented files to be listed\n");
  201.     printf("\t\t/4 causes DOS 4.0 interface to be used\n");
  202.     cfexit(255);
  203.     }
  204.  
  205. _dos_setdrive((*drive-'A')+1, &i);          /* set up the work drive    */
  206. getcwd(cdrive, sizeof(cdrive));         /* get current drive/path    */
  207.  
  208. if (i = chkdrv(*drive))             /* check drive, version err */
  209.     {
  210.     printf("Error: %s", errors[--i]);       /* display any error        */
  211.     cfexit(255);                /* tell the batch job    */
  212.     }
  213.  
  214. get_fat(*drive - 'A');                      /* read FAT into memory     */
  215. dir_search(drive);                /* search for files     */
  216. check_unlinked();                /* check unlinked clusters    */
  217.  
  218. if (files + dirs)                /* q. any files and dirs?    */
  219.     pf = ((long) frag * 100L) /         /* a. yes .. % files frag'd */
  220.               (files + dirs);
  221.  
  222. if (!pf && frag)                /* q. something frag'd      */
  223.     pf = 1;                    /* a. yes .. show non-zero    */
  224.  
  225. printf("\n%d Files, %d Directories,\n",     /* report to user           */
  226.         files, dirs);
  227. printf("%d Unfragmented, %d Fragmented, %d Extra Sections, %d Free Spaces\n",
  228.         unfrag, frag, sections, freespaces);
  229. printf("%d%% of files are fragmented\n\n", pf);
  230.  
  231. switch(option)                    /* return w/errorlevel    */
  232.     {
  233.     case 0:                    /* percentage return    */
  234.     rc = pf;
  235.     break;
  236.  
  237.     case 1:                    /* files return        */
  238.     rc = frag;
  239.     break;
  240.  
  241.     case 2:                    /* extra sections return    */
  242.     rc = sections;
  243.  
  244.     case 3:                    /* freespace areas        */
  245.     rc = freespaces;
  246.     }
  247.  
  248. if (pf == 0)                    /* q. no fragments?     */
  249.     i = 0;                    /* a. yes .. tell 'em       */
  250.  
  251.  else if (pf < 11)                /* q. little fragmentation? */
  252.     i = 1;                    /* a. yes .. set index    */
  253.  
  254.  else if (pf < 76)                /* q. moderate fragm'tion   */
  255.     i = 2;                    /* a. yes .. setup msg    */
  256.  
  257.  else
  258.     i = 3;                    /* ..push the button, Jim    */
  259.  
  260. printf("%s%s\nCHKFRAG finished, Return code %d\n\n%s%s\n",
  261.     "Return type chosen: ", rc_type[option], rc,
  262.     "Suggestion:\n     ", suggestion[i]);
  263.  
  264. cfexit(rc > 254 ? 254 : rc);;            /* return w/errorlevel    */
  265. }
  266.  
  267. /************************************************************************
  268.  *                                    *
  269.  *    get_fat -- read boot record and fat into memory         *
  270.  *                                    *
  271.  ************************************************************************/
  272.  
  273. void    get_fat(drv)
  274. int    drv;                /* drive number         */
  275. {
  276. UINT    i;                /* work             */
  277. long    max_secs,            /* Maximum sectors to read    */
  278.     next_sector,            /* next sector to start read at */
  279.     num_secs;            /* number of sectors to read    */
  280. char    huge *fat_ptr;            /* pointer into fat buffer    */
  281. int    rc;                /* return code work area    */
  282. union    REGS r;             /* work registers        */
  283. struct    SREGS s;            /* ..and work segment regs    */
  284. struct    bootrec
  285.     {
  286.     char jmp[3],            /* jump instruction        */
  287.          oem[8];            /* OEM name            */
  288.     UINT bytes;            /* bytes per sector        */
  289.     char cluster;            /* sectors per cluster        */
  290.     UINT res_sectors;        /* reserved sectors        */
  291.     char fats;            /* number of fats        */
  292.     UINT roots,            /* number of root dir entries    */
  293.          sectors;            /* total sectors        */
  294.     char media;            /* media descriptor block    */
  295.     UINT fatsize,            /* sectors per fat        */
  296.          tracksize,         /* sectors per track        */
  297.          heads;            /* number of heads        */
  298.     long hidden,            /* hidden sectors        */
  299.          sectors_32;        /* sectors if above 32Mb    */
  300.     } far *b_rec;            /* boot record definition    */
  301.  
  302. struct    dos4_i25            /* dos 4.0 int 25 block     */
  303.     {
  304.     long sector;            /* sector to read        */
  305.     int  num_secs;            /* number of sectors to read    */
  306.     char far *read_addr;        /* address of input area    */
  307.     } d4_i25, far *d4_i25p;     /* area and pointer        */
  308.  
  309. char *nomem = "Not enough memory for processing\n";
  310.  
  311. r.h.ah = 0x36;                    /* ah = get freespace    */
  312. r.h.dl = drv + 1;                /* get drive        */
  313. int86(0x21, &r, &r);                /* r.x.cx = bytes/sector    */
  314.  
  315. if ((BOOT = (struct bootrec far *) malloc(r.x.cx)) == NULL)
  316.     {                        /* q. no memory?        */
  317.     printf(nomem);                /* a. yes .. give        */
  318.     cfexit(255);                /* ..error msg/exit     */
  319.     }
  320.  
  321. if (dos4)                    /* dos version 4 interface? */
  322.     {
  323.     r.x.cx = -1;                /* cx = 0xffff        */
  324.     d4_i25.sector = 0L;             /* read sector 0        */
  325.     d4_i25.num_secs = 1;            /* .. for 1 sector        */
  326.     d4_i25.read_addr = (char far *) BOOT;   /* .. into boot record    */
  327.     d4_i25p = &d4_i25;                /* set up pointer        */
  328.     r.x.bx = FP_OFF(d4_i25p);            /* bx = offset of parm block*/
  329.     s.ds   = FP_SEG(d4_i25p);            /* ds = segment of block    */
  330.     }
  331.  else
  332.     {
  333.     r.x.cx = 1;                 /* cx = number of sectors    */
  334.     r.x.dx = 0;                 /* dx = starting sector    */
  335.     r.x.bx = FP_OFF(BOOT);            /* bx = offset of buffer    */
  336.     s.ds   = FP_SEG(BOOT);            /* ds = segment of buffer    */
  337.     }
  338.  
  339. r.h.al = drv;                    /* al = drive number    */
  340. int86x(0x25, &r, &r, &s);            /* read boot sector     */
  341.  
  342. if (r.x.cflag)                    /* q. error reading disk?    */
  343.     {
  344.     printf("Error reading boot record\n");  /* a. yes .. give error msg */
  345.     cfexit(255);                /* ..and return to DOS    */
  346.     }
  347.  
  348. nsectors = (BOOT->sectors ? (long) BOOT->sectors : BOOT->sectors_32);
  349.  
  350. if ((fat = (char huge *) halloc((long) BOOT->fatsize * (long) BOOT->bytes, 1))
  351.                  == (char huge *) NULL)
  352.     {                        /* q. no memory?        */
  353.     printf(nomem);                /* a. yes .. give        */
  354.     cfexit(255);                /* ..error msg/exit     */
  355.     }
  356.  
  357. fat_16 = (nsectors / BOOT->cluster) > 4087; /* set if 16bit FAT tbl */
  358.  
  359. if (dos4)                    /* dos version 4 interface? */
  360.     {
  361.     max_secs = 65536L / BOOT->bytes;        /* max we can read/int 25    */
  362.     fat_ptr = (char far *) fat;            /* initial offset in table    */
  363.     next_sector = BOOT->res_sectors;        /* get first sector of FAT    */
  364.     d4_i25p = &d4_i25;                /* set up pointer        */
  365.  
  366.     for (num_secs = BOOT->fatsize;        /* get number of secs to rd */
  367.         num_secs;)                /* while there are some left*/
  368.     {
  369.     r.x.cx = -1;                /* cx = 0xffff        */
  370.  
  371.     d4_i25.sector = next_sector;        /* read FAT area        */
  372.     d4_i25.read_addr = fat_ptr;        /* point at target of read    */
  373.  
  374.     num_secs -=                /* size of next read ...    */
  375.         (d4_i25.num_secs =            /* .. size of this read    */
  376.            min(max_secs, num_secs));/* .. .. smaller of max, num*/
  377.  
  378.     next_sector += d4_i25.num_secs;        /* Calc next sector number    */
  379.  
  380.     fat_ptr += (long) d4_i25.num_secs * /* Calc next buffer address */
  381.             BOOT->bytes;
  382.  
  383.     r.x.bx = FP_OFF(d4_i25p);        /* bx = offset of parm block*/
  384.     s.ds   = FP_SEG(d4_i25p);        /* ds = segment of block    */
  385.     r.h.al = drv;                /* al = drive number    */
  386.  
  387.     int86x(0x25, &r, &r, &s);        /* read boot sector     */
  388.     }
  389.     }
  390.  else
  391.     {
  392.     r.x.cx = BOOT->fatsize;            /* cx = number of sectors    */
  393.     r.x.dx = BOOT->res_sectors;         /* dx = starting sector    */
  394.     r.x.bx = FP_OFF(fat);            /* bx = offset of buffer    */
  395.     s.ds   = FP_SEG(fat);            /* ds = segment of buffer    */
  396.     r.h.al = drv;                /* al = drive number    */
  397.     int86x(0x25, &r, &r, &s);            /* read boot sector     */
  398.     }
  399.  
  400. if (r.x.cflag)                    /* q. error reading disk?    */
  401.     {
  402.     printf("%02.2x %02.2x Error reading FAT\n",
  403.           r.h.ah, r.x.di);        /* a. yes .. give error msg */
  404.     cfexit(255);                /* ..and return to DOS    */
  405.     }
  406.  
  407. nclusters = (nsectors - (BOOT->res_sectors
  408.         + (BOOT->fatsize * BOOT->fats)
  409.         + ((BOOT->roots * 32) / BOOT->bytes)))
  410.         / BOOT->cluster;
  411.  
  412. printf("Drive %c:%s %lu Sectors, %u Clusters, %u Clustersize\n",
  413.     drv + 'A', readlabel(drv + 'A'),
  414.     nsectors,
  415.     nclusters,
  416.     BOOT->cluster * BOOT->bytes);
  417.  
  418. printf("\nChecking disk structure ..");
  419.  
  420. for(i = 2; i < nclusters;)        /* look for freespaces        */
  421.     {
  422.     if (next_cluster(i, 2, &rc) == 0)    /* q. free?            */
  423.     {
  424.     freespaces++;            /* a. yes. increment free count */
  425.  
  426.     while ((next_cluster(i, 2, &rc) == 0) && ( i < nclusters) )
  427.         i++;            /* skip free spaces        */
  428.  
  429.     }
  430.      else
  431.     i++;                /* else .. check next cluster    */
  432.      }
  433. }
  434.  
  435.  
  436. /************************************************************************
  437.  *                                    *
  438.  *    check_frag -- check a file/directory for fragmentation        *
  439.  *                                    *
  440.  ************************************************************************/
  441.  
  442. void    check_frag(s, n, dflag)
  443. char    *s;                /* file/directory name        */
  444. UINT    n;                /* starting cluster number    */
  445. int    dflag;                /* directory flag        */
  446. {
  447. UINT    i, j;                /* working storage        */
  448. long    nc;                /* next cluster         */
  449. int    flag = 0,            /* flag for frag'd file         */
  450.     rc;                /* error return code        */
  451.  
  452.  
  453. for(; nc = next_cluster(n, 1, &rc); n = nc) /* walk down the chain    */
  454.     {
  455.     if (nc < 0)                 /* q. invalid cluster?    */
  456.     {
  457.     printf("\n\t%s -- %s%s\n%s\n",      /* a. yes .. give err msg   */
  458.         s, rc ? "Invalid cluster detected"
  459.               : "File cross-linked",
  460.         ", Run aborted",
  461.         "\n\t** Please run CHKDSK **");
  462.     cfexit(255);                /* ..and exit w/error code    */
  463.     }
  464.  
  465.     if ((n + 1) != nc)                /* q. non-contiguous area?    */
  466.     {
  467.     flag++;                 /* show fragmented file    */
  468.  
  469.     if (nc > n)                /* q. possibly bad cluster? */
  470.         {
  471.         for (j = n + 1;            /* check for bad spots    */
  472.          next_cluster(j, 0, &rc) == 0xfff7 && j < nc;
  473.          j++);
  474.  
  475.         if (j == i)             /* q. was entire area bad?    */
  476.         flag--;             /* a. yes .. don't report   */
  477.  
  478.          else
  479.         sections++;            /* incr files sections count*/
  480.         }
  481.  
  482.      else
  483.         sections++;             /* incr files sections count*/
  484.     }
  485.     }
  486.  
  487. if (flag)                    /* q. fragmented file    */
  488.     {
  489.     if (NOT frag && list)            /* q. first frag file?    */
  490.     printf("\nFragmented Files/Directories:\n");
  491.  
  492.     if (list)                    /* q. list frag'd files?    */
  493.     printf("%s%s\n",                    /* a. yes .. give it to them*/
  494.         dflag ? "DIR> " : "     ", s);
  495.  
  496.     frag++;                    /* accumulate frag'd count  */
  497.     }
  498.  
  499.  else
  500.     unfrag++;                    /* else total unfrag'd files*/
  501.  
  502. }
  503.  
  504.  
  505. /************************************************************************
  506.  *                                    *
  507.  *    next_cluster -- return next cluster number from FAT        *
  508.  *                                    *
  509.  ************************************************************************/
  510.  
  511. long    next_cluster(n, x, rc)
  512. UINT    n;                /* current cluster number    */
  513. int    x,                /* flag, 1 = reset FAT entry    */
  514.     *rc;                /* error return code        */
  515. {
  516. ULONG    e;                /* entry number in FAT        */
  517. long    nc;                /* next cluster value        */
  518. UINT    huge *p,            /* pointer for 16 bit FAT entry */
  519.     mask1, mask2;            /* mask for and'ing and or'ing  */
  520. int    flag;                /* shift/and flag        */
  521.  
  522.  
  523. *rc = 0;                    /* clear return code    */
  524. nc = n;                     /* initialize nc        */
  525.  
  526. if (! (e = nc))                 /* q. invalid cluster nbr    */
  527.     return(0);                    /* a. yes .. rtn EOF    */
  528.  
  529. if (fat_16)                    /* q. 16 bit FAT entries?    */
  530.     {
  531.     p = (UINT huge *) &fat[0];            /* a. yes .. get FAT addr    */
  532.     nc = p[e];                    /* retrieve next entry    */
  533.  
  534.     if (x == 2)                 /* q. return value?     */
  535.     return(nc);                /* a. yes .. return it    */
  536.  
  537.     if (NOT nc)                 /* q. unallocated cluster?    */
  538.     {
  539.     nc = -1;                /* a. yes .. error condition*/
  540.     *rc = 1;                /* set return code        */
  541.     }
  542.  
  543.     if (x == 1)                 /* q. need to reset entry?    */
  544.     p[e] = 1;                /* a. yes .. show processed */
  545.  
  546.     if (nc >= 0xfff0 && nc != 0xfff7)        /* q. reserved and not bad    */
  547.     nc = 0;                 /* a. yes .. show EOF    */
  548.     }
  549.  
  550.  else
  551.     {
  552.     e = (nc << 1) + nc;             /* cluster number * 3    */
  553.     flag = e & 1;                /* need to do shift later?    */
  554.     e >>= 1;                    /* cluster number * 1.5    */
  555.     nc = *(UINT huge *) &fat[e];        /* get next cluster     */
  556.  
  557.     if (flag)                    /* q. need to do shift?    */
  558.     {
  559.     nc >>= 4;                /* a. yes .. shift by 4 bits*/
  560.     mask1 = 0x000f;             /* mask to clear upper bits */
  561.     mask2 = 0x0010;             /* ..and footprint mask    */
  562.     }
  563.  
  564.      else
  565.     {
  566.     nc &= 0xfff;                /* else .. strip upper bits */
  567.     mask1 = 0xf000;             /* mask to clear lower bits */
  568.     mask2 = 0x0001;             /* ..and footprint mask    */
  569.     }
  570.  
  571.     if (x == 2)                 /* q. return value?     */
  572.     return(nc);                /* a. yes .. return value    */
  573.  
  574.     if (NOT nc)                 /* q. unallocated cluster?    */
  575.     {
  576.     nc = -1;                /* a. yes .. error condition*/
  577.     *rc = 1;                /* set return code        */
  578.     }
  579.  
  580.     if (x == 1)                 /* q. need to reset entry?    */
  581.     {
  582.     *(UINT huge *) &fat[e] &= mask1;    /* a. yes .. 'and' off bits */
  583.     *(UINT huge *) &fat[e] |= mask2;    /* ..and put down footprint */
  584.     }
  585.  
  586.     if (nc >= 0xff0)                /* q. EOF/reserved range?    */
  587.     if (nc == 0xff7)            /* q. bad cluster?        */
  588.         nc = 0xfff7;            /* a. yes .. show bad one    */
  589.      else
  590.         nc = 0;                /* else .. show EOF     */
  591.     }
  592.  
  593. return(nc);
  594. }
  595.  
  596. /************************************************************************
  597.  *                                    *
  598.  *    check_unlinked -- check for unlinked clusters            *
  599.  *                                    *
  600.  ************************************************************************/
  601.  
  602.  
  603. void    check_unlinked()
  604. {
  605. int    rc;                /* error return code        */
  606. UINT    i;                /* loop counter         */
  607. long    j;                /* work return cluster nbr    */
  608.  
  609.  
  610. for (i = 2; i < nclusters; i++)         /* check thru entire FAT    */
  611.     {
  612.     if ((j = next_cluster(i, 2, &rc)) != 0  /* q. unallocated cluster?    */
  613.         && j != 1            /* ..or used        */
  614.         && j != 0xfff7)         /* ..or bad cluster?    */
  615.     {
  616.     printf("\nLost clusters detected, %s%s",/* a. no .. give msg    */
  617.         "Run aborted\n",
  618.         "\t** Please run CHKDSK **\n");
  619.     cfexit(255);                /* ..and exit w/error    */
  620.     }
  621.     }
  622. }
  623.  
  624.  
  625. /************************************************************************
  626.  *                                    *
  627.  *    dir_search -- recursively search all files & subdirectories    *
  628.  *                                    *
  629.  ************************************************************************/
  630.  
  631. void    dir_search(base_dir)
  632. char    *base_dir;            /* base subdirectory to search    */
  633. {
  634. int    oldds,                /* old dta segment        */
  635.     oldda;                /* old dta address        */
  636. char    pass,                /* pass number            */
  637.     work_dir[65],            /* work directory        */
  638.     first_done;            /* find first done        */
  639. struct    fcb find_work;            /* fcb work area        */
  640.  
  641. /*
  642.  *  The following areas are STATIC .. not allocated on recursion
  643.  */
  644.  
  645. static
  646. struct    SREGS    s;            /* segment registers        */
  647.  
  648. static
  649. union    REGS    r;            /* other registers        */
  650.  
  651. static
  652. char    far *cftmp,            /* work pointer         */
  653.     *bc = "\\|/-";                  /* bar characters to use        */
  654.  
  655. static
  656. int    rc,                /* work return code        */
  657.     bar = 0;            /* next bar character        */
  658.  
  659. static
  660. union
  661.     {
  662.     char    dtabuff[128];        /* dta area            */
  663.  
  664.     struct                /* Disk transfer area layout    */
  665.     {
  666.     char dta1[6];            /* first part of dta        */
  667.     char attrib;            /* attribute byte        */
  668.     char drive;            /* drive            */
  669.     char filename[8];        /* filename            */
  670.     char ext[3];            /* extension            */
  671.     char d_attrib;            /* directory attribute        */
  672.     char dta2[10];            /* more reserved space        */
  673.     unsigned
  674.     int  d_time;            /* directory time        */
  675.     unsigned
  676.     int  d_date;            /* directory date        */
  677.     unsigned
  678.     int  d_cluster;         /* first cluster        */
  679.     unsigned
  680.     long d_filesize;        /* size of file         */
  681.     } dta;
  682.     } dta;
  683.  
  684. /*
  685.  *  End of static area
  686.  */
  687.  
  688. r.h.ah = 0x2f;                    /* ah = get dta        */
  689. int86x(0x21, &r, &r, &s);            /* .. ask DOS        */
  690.  
  691. oldds = s.es;                    /* save old DTA segment    */
  692. oldda = r.x.bx;                 /* .. and offset        */
  693.  
  694. cftmp = (char far *) &dta;            /* get current fcb address    */
  695.  
  696. r.h.ah = 0x1a;                    /* ah = set DTA        */
  697. s.ds = FP_SEG(cftmp);                /* ds -> DTA segment    */
  698. r.x.dx = FP_OFF(cftmp);             /* ds:dx -> DTA        */
  699. int86x(0x21, &r, &r, &s);            /* setup new DTA        */
  700.  
  701. if (strcmp(base_dir, translate_name(base_dir))) /* q. JOIN'd?           */
  702.     return;                    /* a. yes .. skip it    */
  703.  
  704. chdir(base_dir);                /* get the base directory    */
  705.  
  706. for(first_done=pass=0;;)            /* look through current dir */
  707.     {
  708.     if (first_done == 0)            /* q. find first done?    */
  709.     {                    /* a. no .. do it        */
  710.  
  711.     if (base_dir[1] == ':')             /* q. disk specified?       */
  712.         find_work.drive =            /* a. yes .. set fcb drive    */
  713.             ((base_dir[0] & 0xdf) - 'A') + 1;
  714.      else
  715.         find_work.drive = 0;        /* else use default     */
  716.  
  717.     find_work.hexff = 0xff;         /* extended fcb        */
  718.     CLEAR(find_work.extra, 0);        /* set extra area        */
  719.     CLEAR(find_work.filename, '?');     /* .. and file name         */
  720.     CLEAR(find_work.ext, '?');          /* .. and extension         */
  721.     find_work.attrib = FILES;        /* set up attribute to find */
  722.  
  723.     r.h.ah = 0x11;                /* ah = find first        */
  724.     cftmp = (char far *) &find_work;    /* get pointer to work fcb    */
  725.     s.ds = FP_SEG(cftmp);            /* ds -> segment of fcb    */
  726.     r.x.dx = FP_OFF(cftmp);         /* ds:dx -> offset        */
  727.     int86x(0x21, &r, &r, &s);        /* .. find first        */
  728.  
  729.     rc = r.h.al;                /* get return code        */
  730.  
  731.     first_done = 1;             /* first find done        */
  732.     }
  733.  
  734.      else
  735.     {
  736.     r.h.ah = 0x12;                /* ah = find next        */
  737.     cftmp = (char far *) &find_work;    /* get pointer to work fcb    */
  738.     s.ds = FP_SEG(cftmp);            /* ds -> segment of fcb    */
  739.     r.x.dx = FP_OFF(cftmp);         /* ds:dx -> offset        */
  740.     int86x(0x21, &r, &r, &s);        /* .. find first        */
  741.  
  742.     rc = r.h.al;                /* get return code        */
  743.     }
  744.  
  745.     if (NOT list)                /* q. list in progress?    */
  746.     {                    /* a. no ..         */
  747.     fprintf(stderr, "%c%c", bc[bar], 8);/* print bar, backspace     */
  748.     if (++bar > 3)                /* q. limit on chars?    */
  749.         bar = 0;                /* a. yes .. reset to zero    */
  750.     }
  751.  
  752.     strcpy(work_dir, base_dir);         /* get current base     */
  753.  
  754.     if (work_dir[strlen(work_dir)-1] != '\\')   /* if needed ..         */
  755.      strcat(work_dir, "\\");                /* .. add a backslash   */
  756.  
  757.     strcat(work_dir,                /* .. add the name        */
  758.      fname(dta.dta.filename, dta.dta.ext));
  759.  
  760.     if (pass)                    /* q. second pass?        */
  761.     {
  762.     if (rc)                 /* q. more files found?    */
  763.         break;                /* a. no .. exit        */
  764.  
  765.     if (!(dta.dta.d_attrib & _A_SUBDIR)    /* q. directory?    */
  766.           || (dta.dta.filename[0] == '.'))  /* .. or a dot dir?     */
  767.           continue;             /* a. get next entry    */
  768.  
  769.     dirs++;                 /* accumulate dir count    */
  770.     dir_search(work_dir);            /* recursively call ourself */
  771.     }
  772.  
  773.      else                    /* first pass processing    */
  774.     {
  775.     if (rc)                 /* q. anything found?    */
  776.         {                    /* a. no ..         */
  777.         first_done = 0;            /* re-execute find-first    */
  778.         pass++;                /* go to next pass        */
  779.         continue;                /* .. continue processing    */
  780.         }
  781.  
  782.     if (dta.dta.filename[0] == '.')     /* q. dot directory?        */
  783.         continue;                /* a. yes .. skip it    */
  784.  
  785.     if (!(dta.dta.d_attrib & _A_SUBDIR))    /* q. a file?        */
  786.         files++;                /* a. yes .. count them */
  787.  
  788.     check_frag(work_dir,            /* check for frag'd file    */
  789.         dta.dta.d_cluster,
  790.         dta.dta.d_attrib & _A_SUBDIR);
  791.     }
  792.     }
  793.  
  794. r.h.ah = 0x1a;                    /* ah = set DTA        */
  795. s.ds = oldds;                    /* ds -> DTA segment    */
  796. r.x.dx = oldda;                 /* ds:dx -> DTA        */
  797. int86x(0x21, &r, &r, &s);            /* setup new DTA        */
  798.  
  799. }
  800.  
  801.  
  802. /************************************************************************
  803.  *                                    *
  804.  *    fname -- build a normalized filename from an FCB        *
  805.  *                                    *
  806.  ************************************************************************/
  807.  
  808. char    *fname(filename, ext)
  809. char    *filename;            /* filename with trailing blanks*/
  810. char    *ext;                /* extension            */
  811. {
  812. int    i;                /* loop control         */
  813. char    *p;                /* work pointer         */
  814. static
  815. char    fwork[13];            /* returned work area        */
  816.  
  817.  
  818. p = fwork;                    /* initialize string pointer*/
  819.  
  820. for (i = 0; (i < 8) && (*filename != ' '); i++) /* move fname w/o blanks*/
  821.     *p++ = *filename++;
  822.  
  823. if (*ext != ' ')                            /* q. extension blank?      */
  824.     {
  825.     *p++ = '.';                             /* a. no .. add the dot     */
  826.  
  827.     for (i = 0; (i < 3) && (*ext != ' '); i++)  /* add ext w/o blanks   */
  828.     *p++ = *ext++;
  829.     }
  830.  
  831. *p = 0;                     /* terminate string w/null    */
  832.  
  833. return(fwork);                    /* return string to caller    */
  834.  
  835. }
  836.  
  837.  
  838. /************************************************************************
  839.  *                                    *
  840.  *    strclr -- clear an area to a value                *
  841.  *                                    *
  842.  ************************************************************************/
  843.  
  844. void    strclr(s, c, n)
  845. char    *s;                /* area to initialize        */
  846. int    c,                /* value to use         */
  847.     n;                /* length of area to clear    */
  848. {
  849.  
  850. while(n--)                    /* initialize whole area    */
  851.     *s++ = c;                    /* ..to value specified    */
  852.  
  853. }
  854.  
  855.  
  856. /************************************************************************
  857.  *                                    *
  858.  *    translate_name --  translate a DOS name             *
  859.  *                                    *
  860.  ************************************************************************/
  861.  
  862. char    *translate_name(name)
  863. char    far *name;            /* name to translate        */
  864. {
  865. static
  866. char    translate_area[65],        /* work/return area        */
  867.     far *sp;            /* work pointer         */
  868.  
  869. union    REGS r;             /* work registers        */
  870. struct    SREGS s;            /* ..and work segment regs    */
  871.  
  872.  
  873. r.h.ah = 0x60;                /* ah = translate        */
  874.  
  875. sp = (char far *) name;         /* set up a pointer ..        */
  876. r.x.si = FP_OFF(sp);            /* set pointer to input name    */
  877. s.ds = FP_SEG(sp);            /* .. and segment        */
  878.  
  879. sp = (char far *) translate_area;    /* set up a pointer ..        */
  880. r.x.di = FP_OFF(sp);            /* set pointer to output area    */
  881. s.es = FP_SEG(sp);            /* .. and segment        */
  882. int86x(0x21, &r, &r, &s);        /* translate the name        */
  883.  
  884. if (r.x.cflag)                /* if bad name ..        */
  885.     return((char far *) NULL);        /* .. return error        */
  886.  
  887.  else
  888.     return(translate_area);        /* return xlated name        */
  889. }
  890.  
  891.  
  892. /************************************************************************
  893.  *                                    *
  894.  *    chkdrv -- assure drive is LOCAL and not SUBST'd or ASSIGN'd     *
  895.  *                                    *
  896.  ************************************************************************/
  897.  
  898. int    chkdrv(c)
  899. char    c;                /* drive to check        */
  900. {
  901.  
  902. static
  903. char    wdrv[] = " :\\";                /* work area for drive name     */
  904.  
  905. union    REGS r;             /* work registers        */
  906. struct    SREGS s;            /* ..and work segment regs    */
  907.  
  908. if (_osmajor < 2)                /* q. pre-DOS 2.00?     */
  909.     return(4);                    /* a. yes .. can't run it   */
  910.  
  911. if (_osmajor >= 3 && _osminor >= 1)        /* q. DOS 3.1 or higher?    */
  912.     {
  913.     r.x.ax = 0x4409;                /* ah = ioctl, local test    */
  914.     r.h.bl = (c - 'A') + 1;                 /* bl = drive to test       */
  915.     int86(0x21, &r, &r);            /* test drive        */
  916.  
  917.     if (r.x.cflag)                /* q. bad drive?        */
  918.     return(1);                /* a. yes .. error        */
  919.  
  920.     if (r.x.dx & 0x1000)            /* q. remote?        */
  921.     return(2);                /* a. yes .. error        */
  922.  
  923.     wdrv[0] = c;                /* set up name        */
  924.  
  925.     if (strcmp(wdrv, translate_name(wdrv))) /* q. SUBST or ASSIGNED?    */
  926.     return(3);                /* a. yes .. return error    */
  927.     }
  928.  
  929. dos4 |= (_osmajor > 3);             /* check for dos v.4    */
  930.  
  931. return(0);                    /* return ok        */
  932.  
  933. }
  934.  
  935.  
  936. /************************************************************************
  937.  *                                    *
  938.  *    Read drive label, if available                    *
  939.  *                                    *
  940.  ************************************************************************/
  941.  
  942. char    *readlabel(c)
  943. int    c;                /* drive to check        */
  944. {
  945. char    *p, *q;             /* work pointers        */
  946. struct    find_t f;            /* structure for directory entry*/
  947.  
  948. static
  949. char    work_dir[13] = { " :\\*.*" } ;  /* directory to check           */
  950.  
  951.  
  952. work_dir[0] = c;                /* setup for find first    */
  953.  
  954. if (_dos_findfirst(work_dir, LABEL, &f))    /* q. error on label get?    */
  955.     work_dir[0] = 0;                /* a. yes .. then no label    */
  956.  
  957.  else
  958.     {
  959.     for(p = work_dir, q = f.name; *q; q++)  /* copy label w/o middle .    */
  960.     if (*q != '.')                      /* q. is this char a dot?   */
  961.         *p++ = *q;                /* a. no .. copy it     */
  962.  
  963.     *p = 0;                    /* terminate string     */
  964.     }
  965.  
  966. return(work_dir);                /* ..and return label string*/
  967.  
  968. }
  969.  
  970. /************************************************************************
  971.  *                                    *
  972.  *    cfexit() - return to DOS after resetting dir, dir        *
  973.  *                                    *
  974.  ************************************************************************/
  975.  
  976. void    cfexit(rc)
  977. int    rc;                /* return code to exit with    */
  978.  
  979. {
  980. int    i;                /* work variable        */
  981.  
  982. _dos_setdrive(sdrive, &i);            /* reset the default drive    */
  983. chdir(cdrive);                    /* .. and directory     */
  984.  
  985. exit(rc);                    /* .. and return to DOS    */
  986. }
  987. 
  988.